#include "lib.h"
#include <memory.h>

#include "vpguts.h"
#include "vphand.h"
#include "shue.h"
#include "vpsaved.h"
#include "cardmgr.h"
#include "vpconn.h"

#include "jbtr.h"
#include "jokp.h"
#include "wld2.h"
#include "ace8.h"
#include "amer.h"
#include "bonp.h"
#include "bpdx.h"
#include "dbnp.h"
#include "ddbp.h"
#include "ddjp.h"
#include "djpk.h"
#include "ldeu.h"
#include "wld7.h"
#include "bw2.h"
#include "mystery.h"

#ifndef NULL
#ifdef __cplusplus
#define NULL    0
#else  /* __cplusplus */
#define NULL    ((void *)0)
#endif  /* __cplusplus */
#endif  /* NULL */

//
// The order here MUST match the SQL machId
//

#define NUMGUTS	(sizeof(sGuts) / sizeof(sGuts[0]))

static VPGuts* sGuts[] =
{
	new JacksOrBetterGame(VPGuts::LOW),
	new JacksOrBetterGame(VPGuts::MEDIUM),
	new JacksOrBetterGame(VPGuts::HIGH),
	NULL,	// CSN-6225: custom paytable only implemented for Double Double Jackpot Poker

	new JokerPokerGame(VPGuts::LOW),
	new JokerPokerGame(VPGuts::MEDIUM),
	new JokerPokerGame(VPGuts::HIGH),
	NULL,	// CSN-6225: custom paytable only implemented for Double Double Jackpot Poker

	new DeucesWildGame(VPGuts::LOW),
	new DeucesWildGame(VPGuts::MEDIUM),
	new DeucesWildGame(VPGuts::HIGH),
	NULL,	// CSN-6225: custom paytable only implemented for Double Double Jackpot Poker

	new AcesOrEightsGame(VPGuts::LOW),
	new AcesOrEightsGame(VPGuts::MEDIUM),
	new AcesOrEightsGame(VPGuts::HIGH),
	NULL,	// CSN-6225: custom paytable only implemented for Double Double Jackpot Poker

	new AllAmericanGame(VPGuts::LOW),
	new AllAmericanGame(VPGuts::MEDIUM),
	new AllAmericanGame(VPGuts::HIGH),
	NULL,	// CSN-6225: custom paytable only implemented for Double Double Jackpot Poker

	new BonusPokerGame(VPGuts::LOW),
	new BonusPokerGame(VPGuts::MEDIUM),
	new BonusPokerGame(VPGuts::HIGH),
	NULL,	// CSN-6225: custom paytable only implemented for Double Double Jackpot Poker

	new BonusPokerDeluxeGame(VPGuts::LOW),
	new BonusPokerDeluxeGame(VPGuts::MEDIUM),
	new BonusPokerDeluxeGame(VPGuts::HIGH),
	NULL,	// CSN-6225: custom paytable only implemented for Double Double Jackpot Poker

	new DoubleBonusPokerGame(VPGuts::LOW),
	new DoubleBonusPokerGame(VPGuts::MEDIUM),
	new DoubleBonusPokerGame(VPGuts::HIGH),
	NULL,	// CSN-6225: custom paytable only implemented for Double Double Jackpot Poker

	new DoubleDoubleBonusPokerGame(VPGuts::LOW),
	new DoubleDoubleBonusPokerGame(VPGuts::MEDIUM),
	new DoubleDoubleBonusPokerGame(VPGuts::HIGH),
	NULL,	// CSN-6225: custom paytable only implemented for Double Double Jackpot Poker

	new DoubleDoubleJackpotPokerGame(VPGuts::LOW),
	new DoubleDoubleJackpotPokerGame(VPGuts::MEDIUM),
	new DoubleDoubleJackpotPokerGame(VPGuts::HIGH),
	new DoubleDoubleJackpotPokerGame(VPGuts::CUSTOM),	// CSN-6225

	new DoubleJackpotPokerGame(VPGuts::LOW),
	new DoubleJackpotPokerGame(VPGuts::MEDIUM),
	new DoubleJackpotPokerGame(VPGuts::HIGH),
	NULL,	// CSN-6225: custom paytable only implemented for Double Double Jackpot Poker

	new LooseDeucesGame(VPGuts::LOW),
	new LooseDeucesGame(VPGuts::MEDIUM),
	new LooseDeucesGame(VPGuts::HIGH),
	NULL,	// CSN-6225: custom paytable only implemented for Double Double Jackpot Poker

	new SevensWildGame(VPGuts::LOW),
	new SevensWildGame(VPGuts::MEDIUM),
	new SevensWildGame(VPGuts::HIGH),
	NULL,	// CSN-6225: custom paytable only implemented for Double Double Jackpot Poker

	new BonusDeucesWildGame(VPGuts::LOW),
	new BonusDeucesWildGame(VPGuts::MEDIUM),
	new BonusDeucesWildGame(VPGuts::HIGH),
	NULL,	// CSN-6225: custom paytable only implemented for Double Double Jackpot Poker

    NULL,   // Pick'em Poker was not implemented...
    NULL,   // Pick'em Poker was not implemented...
    NULL,   // Pick'em Poker was not implemented...
	NULL,	// CSN-6225: custom paytable only implemented for Double Double Jackpot Poker

	new MysteryGame(VPGuts::LOW),
	new MysteryGame(VPGuts::MEDIUM),
	new MysteryGame(VPGuts::HIGH),
	NULL	// CSN-6225: custom paytable only implemented for Double Double Jackpot Poker
};

VPGuts*
VPGuts::getGuts(int32 ID, int32 whichFreq)
{
	return (VPGuts*)(ID * NUMFREQS + whichFreq < NUMGUTS
		? sGuts[ID * NUMFREQS + whichFreq]
		: 0);
}

void
VPGuts::dealHand(Shue* shue, VPHand* hand)
{
	hand->add(shue->getCard());
	hand->add(shue->getCard());
	hand->add(shue->getCard());
	hand->add(shue->getCard());
	hand->add(shue->getCard());
}

void
VPGuts::makeDealtHandForClient(VPHand* src, VPHand* dest)
{
	int32 i;

	for (i = 0; i < src->getCount(); i++)
		dest->add(src->getCardAt(i));
}

void
VPGuts::rollbackDeal(Shue* shue, VPHand* hand)
{
	shue->pushBackNCards(hand->getCount());
	hand->removeAll();
}

bool8
VPGuts::isHoldTheseValid(uint8 held)
{
	return (bool8)((held & ~0x1f) == 0);
}

void
VPGuts::drawHand(uint8 held, Shue* shue, VPHand* hand)
{
	int32 i;

	for (i = 0; i < 5; i++)
		if ((held & (1 << i)) == 0)
			hand->replace(i, shue->getCard());
}

// here I have to make sure that the new (drawn)
// hand will not contain ANY CARD from Dealt hand
bool8
VPGuts::drawHandCheck(uint8 *dealtCards, uint8 held, Shue* shue, VPHand* hand)
{
#ifndef FORCEDSHOE

	int32 i;

	// draw and check
	for (i = 0; i < 5; i++)
		if ((held & (1 << i)) == 0)
		{
			do
			{
				uint8 newcard = shue->getCard();

				for( int j=0; j<5; j++ )
					if( newcard == dealtCards[j] ) break;

				if( j==5 )
				{
					hand->replace(i, newcard);
					break;
				}

			} while(true);
		}

	return true;

#else // FORCEDSHOE

	// CSN-13015 - Debug version of drawHandCheck() checks that the shue is went over only once for
	//             each card to be replaced so that the server doesn't fall into an infinite loop
	//             in case the forced shue doesn't have enough number of different cards.

	int32 i, j;
	bool8 isHandDrawn = true;

	// Draw and check each card in hand
	for (i = 0; i < 5 && isHandDrawn; i++)
	{
		// Check that the card is to be replaced
		if ((held & (1 << i)) == 0)
		{
			bool8 isCardReplaced = false;
			int32 numCards = 0; // New cards counter

			do
			{
				// Get a new card from shue
				uint8 card = shue->getCard();
				numCards++;

				// Check that the new card is not in the dealt hand
				for (j = 0; j < 5; j++)
				{
					if (card == dealtCards[j])
					{
						DEBUGMSG(("Card %x found in the dealt hand. Getting another one.", card));
						break;
					}
				}

				// Replace new card in the hand
				if (j == 5)
				{
					hand->replace(i, card);
					isCardReplaced = true;
				}
				else if (numCards == shue->getNumCards())
				{
					// The card couldn't be replaced
					DEBUGMSG(("ERROR: Card %x couldn't be replaced. Failed to draw hand.", card));
					isHandDrawn = false;
					break;
				}
			}
			while (!isCardReplaced);
		}
	}

	return isHandDrawn;

#endif // FORCEDSHOE
}

void
VPGuts::makeDrawnHandForClient(VPHand* src, VPHand* dest)
{
	int32 i;

	for (i = 0; i < src->getCount(); i++)
		dest->add(src->getCardAt(i));
}

void
VPGuts::rollbackDraw(uint8 held, Shue* shue, VPHand* hand)
{
	int32 i, numDrawn;

	for (numDrawn = i = 0; i < 5; i++)
		if ((held & (1 << i)) == 0)
			++numDrawn;

	shue->pushBackNCards(numDrawn);
	rollbackDeal(shue, hand);
	dealHand(shue, hand);
}

int32
VPGuts::doubleUpCompare(uint8 dealer, uint8 player)
{
	int32 dRank, pRank;

	//
	// NOTE WELL:  This function only works on a standard 52-card deck
	//			   without jokers or wild cards.
	//

	dRank = getCardMgr()->getRank(dealer);
	pRank = getCardMgr()->getRank(player);

	return dRank - pRank;
}

void
VPGuts::restoreHand(Shue* shue, VPHand* hand, VPokerSavedGame* saved)
{
	hand->restore(saved->cards, VPokerSavedGame::MAXCARDS);
}

void
VPGuts::createSubGameData(uint8* subGameData, int32* length, int32 maxlength)
{
	*length = 0;
}

void VPGuts::restoreSubGameData(uint8* subGameData, int32* length)
{
}

uint8
VPGuts::restoreHeldCards(uint8* dealt, uint8* drawn, int32 num)
{
	uint8 result = 0;
	int32 i;

	for (i = 0; i < num; i++)
		if (dealt[i] == drawn[i])
			result |= (1 << i);

	return result;
}

//////////////////////////////////////////////////////////////////////////////
// CleaupGuts cleans sGuts[] to avoid false reports of memory leaks.
//////////////////////////////////////////////////////////////////////////////
static 
class CleanupGuts
{
public:
	~CleanupGuts()
	{
		for (int i = 0; i < NUMGUTS; ++i )
		{
			delete sGuts[i];
			sGuts[i] = 0;
		}
	}
} autoRunCleanupGutsOnExit;
